package com.chengzhi.structure;


/**
 * @description: 双向链表
 * @author: ruanchengzhi
 * @create: 2020-08-20 11:10
 **/
public class DoubleLinkedList<E> implements BaseLinkedList<E> {

    private Node<E> head;

    private Node<E> tail;

    private int size;

    @Override
    public void add(E value) {
        Node pre = tail;
        Node node = new Node(value, tail, null);
        tail = node;
        if (pre == null) {
            head = node;
        } else {
            pre.next = node;
        }
        size++;
    }

    @Override
    public void add(int index, E value) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException("index > size");
        }
        if (index == size) {
            add(value);
        } else {
            Node<E> cur = node(index);
            Node<E> pred = cur.prev;
            Node<E> newNode = new Node<>(value, pred, cur);
            cur.prev = newNode;
            if (pred == null) {
                head = newNode;
            } else {
                pred.next = newNode;
            }
            size++;
        }
    }

    Node<E> node(int index) {
        if (index < (size >> 1)) {
            Node<E> x = head;
            for (int i = 0; i < index; i++) {
                x = x.next;
            }
            return x;
        } else {
            Node<E> x = tail;
            for (int i = size - 1; i > index; i--) {
                x = x.prev;
            }
            return x;
        }
    }

    @Override
    public boolean delete(E value) {
        if (size == 0) {
            throw new IllegalArgumentException("is empty");
        }
        for (Node<E> x = head; x != null; x = x.next) {
            if (value.equals(x.value)) {
                unlink(x);
                return true;
            }
        }
        return false;
    }

    private void unlink(Node<E> x) {
        Node<E> next = x.next;
        Node<E> prev = x.prev;
        if (prev == null) {
            head = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
        if (next == null) {
            tail = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }
        x.value = null;
        size--;
    }

    @Override
    public boolean contains(E value) {
        for (Node<E> x = head; x != null; x = x.next) {
            if (value.equals(x.value)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int getSize() {
        return size;
    }

    public void print() {
        Node root = head;
        while (null != root) {
            System.out.print(root.value + " ");
            root = root.next;
        }
        System.out.println();
    }


    class Node<E> {
        E value;
        Node<E> prev;
        Node<E> next;

        public Node(E value) {
            this.value = value;
        }

        public Node(E value, Node<E> prev) {
            this.value = value;
            this.prev = prev;
        }

        public Node(E value, Node<E> prev, Node<E> next) {
            this.value = value;
            this.prev = prev;
            this.next = next;
        }
    }
}
